/*
 * Copyright 2007-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.paoding.rose.web.impl.module;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import net.paoding.rose.web.ControllerErrorHandler;
import net.paoding.rose.web.ControllerInterceptor;
import net.paoding.rose.web.annotation.DefaultController;
import net.paoding.rose.web.annotation.ReqMethod;
import net.paoding.rose.web.impl.mapping.Mapping;
import net.paoding.rose.web.impl.mapping.MappingImpl;
import net.paoding.rose.web.impl.mapping.MatchMode;
import net.paoding.rose.web.paramresolver.Resolver;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.multipart.MultipartResolver;

/**
 * {@link Module}的实现
 * 
 * @author zhiliang.wang [qieqie.wang@paoding.net]
 */
public class ModuleBean implements Module {

    private static Log logger = LogFactory.getLog(ModuleBean.class);

    // 该模块的映射地址
    private String path;

    private XmlWebApplicationContext applicationContext;

    // 本module下的controller以及附属信息
    private List<Mapping<ControllerInfo>> controllerMappings = new ArrayList<Mapping<ControllerInfo>>();

    // 
    private List<Resolver> customerResolvers = new ArrayList<Resolver>();

    // 用于add方法加进来，最终==>到下面的interceptors
    private ArrayList<NamedControllerInterceptor> interceptorList;

    // 该模块的拦截器，调用get方法时候，由interceptorList.toArray进来,返回时保证非null
    private NamedControllerInterceptor[] interceptors;

    // 本模块的ErrorHanlder
    private ControllerErrorHandler errorHandler;

    // 默认的控制器，会先看看有没有@Path("")标注的或@DefaultController标注的，
    // 没有的话则按照候选方案看看有没有default,index,home,welcome的控制器，有的话就是它了
    private Mapping<ControllerInfo> defaultController;

    private boolean defaultControllerCached;

    //
    private MultipartResolver multipartResolver;

    public ModuleBean(String path, XmlWebApplicationContext applicationContext) {
        this.path = path;
        this.applicationContext = applicationContext;
    }

    @Override
    public String getPath() {
        return path;
    }

    @Override
    public XmlWebApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @Override
    public Mapping<ControllerInfo> getDefaultController() {
        if (!defaultControllerCached) {
            if (defaultController == null) {
                for (Mapping<ControllerInfo> info : controllerMappings) {
                    if (info.getTarget().getControllerClass()
                            .getAnnotation(DefaultController.class) != null) {
                        defaultController = info;
                        logger.info("module '" + getPath() + "': found default controller '"
                                + defaultController + "'("
                                + info.getTarget().getControllerClass().getName()
                                + ") by annotation @" + DefaultController.class.getSimpleName());
                        break;
                    }
                }
            }
            if (defaultController == null) {
                String[] candidates = { "", "/default", "/index", "/home", "/welcome", "/hello" };
                for (String candidate : candidates) {
                    for (Mapping<ControllerInfo> info : controllerMappings) {
                        if (candidate.equals(info.getPath())) {
                            defaultController = info;
                            logger.info("module '" + getPath() + "': found default controller '"
                                    + defaultController + "'("
                                    + info.getTarget().getControllerClass().getName()
                                    + ")  by sacnning controller by convention.");
                            break;
                        }

                    }
                }
            }
            if (defaultController == null) {
                if (logger.isWarnEnabled()) {
                    logger.warn("module '" + getPath() + "': nout found"
                            + " a default controller by annotation" + " or by convention.");
                }
            }
            defaultControllerCached = true;
        }
        return defaultController;
    }

    public void setDefaultController(Mapping<ControllerInfo> defaultController) {
        this.defaultController = defaultController;
    }

    @Override
    public List<Mapping<ControllerInfo>> getControllerMappings() {
        List<Mapping<ControllerInfo>> controllerInfos = new ArrayList<Mapping<ControllerInfo>>(
                this.controllerMappings.size());
        controllerInfos.addAll(this.controllerMappings);
        Collections.sort(controllerInfos);
        return Collections.unmodifiableList(controllerInfos);
    }

    public ModuleBean addController(String controllerPath, ReqMethod[] methods,
            Class<?> controllerClass, Object controller) {
        ControllerInfo contr = new ControllerInfo();
        contr.setControllerClass(controllerClass);
        contr.setControllerObject(controller);
        this.controllerMappings.add(new MappingImpl<ControllerInfo>(controllerPath,
                MatchMode.PATH_STARTS_WITH, methods, contr));
        return this;
    }

    public ModuleBean addControllerInterceptor(String name, ControllerInterceptor interceptor) {
        NamedControllerInterceptor namedInterceptor = new NamedControllerInterceptor(name,
                interceptor);
        return addNamedControllerInterceptor(namedInterceptor);
    }

    public ModuleBean addCustomerResolver(Resolver resolver) {
        customerResolvers.add(resolver);
        return this;
    }

    public List<Resolver> getCustomerResolvers() {
        return Collections.unmodifiableList(customerResolvers);
    }

    public ModuleBean addNamedControllerInterceptor(NamedControllerInterceptor namedInterceptor) {
        if (interceptorList == null) {
            interceptorList = new ArrayList<NamedControllerInterceptor>();
        }
        for (int i = 0; i < interceptorList.size(); i++) {
            if (namedInterceptor.getPriority() > interceptorList.get(i).getPriority()) {
                this.interceptorList.add(i, namedInterceptor);
                return this;
            }
        }
        this.interceptorList.add(namedInterceptor);
        interceptors = null;
        return this;
    }

    @Override
    public NamedControllerInterceptor[] getInterceptors() {
        if (interceptors == null) {
            if (interceptorList == null) {
                interceptors = new NamedControllerInterceptor[0];
            } else {
                interceptors = interceptorList
                        .toArray(new NamedControllerInterceptor[interceptorList.size()]);
            }
        }
        return interceptors;
    }

    @Override
    public ControllerErrorHandler getErrorHandler() {
        return errorHandler;
    }

    public void setErrorHandler(ControllerErrorHandler errorHandler) {
        this.errorHandler = errorHandler;
    }

    @Override
    public MultipartResolver getMultipartResolver() {
        return multipartResolver;
    }

    public void setMultipartResolver(MultipartResolver multipartResolver) {
        this.multipartResolver = multipartResolver;
    }
}
